#include "SoundSystem.h"

#include <assert.h>

SoundSystem* Simpleton<SoundSystem>::s_singleton=0;

void error_callback(const char* str, bool is_critical)
{
		if (is_critical)
			Ogre::LogManager::getSingletonPtr()->logMessage("!!Sound System error: "+Ogre::String(str));
		else
			Ogre::LogManager::getSingletonPtr()->logMessage("!!Sound System warning: "+Ogre::String(str));
}

SoundSystem::SoundSystem(void)
{
	m_pListener=0;
	//pint to log
	Ogre::LogManager::getSingletonPtr()->logMessage("**************************************");
	Ogre::LogManager::getSingletonPtr()->logMessage("*** INITIALIZING SOUND SYSTEM ***");
	Ogre::LogManager::getSingletonPtr()->logMessage("**************************************");

	fslSetErrorCallback(error_callback);

	if (!fslInit(FSL_SS_EAX2))
	{
		error_callback("EAX 2.0 init failed",false);
		if (!fslInit(FSL_SS_ALUT))
		{
			error_callback("init failed",true);
			assert(false);	//this should never happen!
		}
	}
	//init source manager
	fslInitSourceManager(fslGetMaxSources());

	fslSetListenerPosition(0.0f,0.0f,0.0f);
	fslSetListenerOrientation(0.0f,0.0f,-1.0f,0.0f,1.0f,0.0f);
	fslSetMetersPerUnit(1.0f);
	fslSetListenerDistanceModel(FSL_EXPONENT_DISTANCE);
	fslSetListenerEnvironmentPreset(FSL_ENVIRONMENT_UNDERWATER);
	fslSetDopplerParameters(0.3f,1.0f);
	m_bPauseAll =false;
}

SoundSystem::~SoundSystem(void)
{
	Ogre::LogManager::getSingletonPtr()->logMessage("*** DESTROYING SOUND SYSTEM ***");

	unloadAllSounds();
	/*
		Shut down
	*/
	fslShutDownSourceManager();
	fslShutDown();
}

void SoundSystem::unloadAllSounds()
{
	fslSoundStopAllSounds();
	//unload the sounds from the map
	/*if(!m_FileNameBufferMap.empty())
	{
		std::map<std::string, FSLbuffer>::iterator iter;

		for(iter=m_FileNameBufferMap.begin();iter!=m_FileNameBufferMap.end();iter++)
		{
			fslDestroyBuffer(iter->second);
			iter->second=0;
		}
	}*/

	fslFreeAllBuffers();

	m_FileNameBufferMap.clear();
}

void SoundSystem::loadSound(const char* filePath,bool bStreamed)
{
	Ogre::String path;
	if(!m_strFileSystem.empty())
	{
		path=m_strFileSystem+filePath;

		filePath=path.c_str();
	}
	if(m_FileNameBufferMap[filePath])
		return;
	FSLbuffer buffer;

	//if not streamed then load normally or load the streamed version
	if(!bStreamed)
	{
		buffer = fslLoadBuffer(filePath);
		if(buffer)
			m_FileNameBufferMap[filePath] = buffer;
		else
			error_callback("loadSound - buffer is null, is the path correct?",false);
	}else
	{
		assert(false);	//not implemented yet
	}
}

const FSLsound* SoundSystem::playSound(const char* file,bool bLooping,bool bPaused)
{
		FSLsound sound;
		const FSLbuffer* buffer = getBuffer(file);
		if(buffer)
		{
			FSLsound sound = fslSourceManagerGetSound(*buffer);
			if(sound)
			{
				fslSoundSetLooping(sound,bLooping);
				if(bPaused)
					fslSoundPause(sound);

				fslSoundPlay(sound);

				m_Sounds.push_back(sound);
				return &m_Sounds.back();
			}else
				error_callback("playSound failed: no more free sources",false);
		}else
			error_callback("playSound failed: is the file name correct?",false);

		return 0;
}

const FSLbuffer* SoundSystem::getBuffer(const char* filePath)const
{
	Ogre::String path;
	if(!m_strFileSystem.empty())
	{
		path=m_strFileSystem+filePath;

		filePath=path.c_str();
	}

	return &(m_FileNameBufferMap.at(filePath));
}

const FSLsound* SoundSystem::play3DSound(const char* file,const Ogre::Vector3& pos,const Ogre::Vector3& velocity,const float& size,const bool& bLooping,const bool& bPaused)
{
	FSLsound sound;
	const FSLbuffer* buffer = getBuffer(file);
	if(buffer)
	{
		sound = fslSourceManagerGetSound(*buffer);
		if(sound)
		{
			fslSoundSetLooping(sound,bLooping);
			if(bPaused)
				fslSoundPause(sound);

			fslSoundSetPosition(sound,pos.x,pos.y,pos.z);

			fslSoundSetVelocity(sound,velocity.x,velocity.y,velocity.z);

			fslSoundSetMaxDistance(sound,size);
			fslSoundSetRolloff(sound,1.0f);

			fslSoundPlay(sound);

			m_Sounds.push_back(sound);
			return &m_Sounds.back();
		}else
			error_callback("playSound failed: no more free sources",false);
	}else
		error_callback("playSound failed: is the file name correct?",false);
	return 0;
}

void SoundSystem::stopSound(const FSLsound* sound)
{
	assert(sound);
	fslSoundStop(*sound);
}

void SoundSystem::update(float fElapsedTime)
{
	//update the listner
	if(m_pListener)
	{
		//setListener(m_pListener->getPos(),m_pListener->getForward(),m_pListener->getUp(),m_pListener->getVel());
		//m_pListener->update(fElapsedTime);
		Ogre::Vector3 temp = m_pListener->getPos();
		fslSetListenerPosition(temp.x,temp.y,temp.z);

		temp = m_pListener->getVel();
		fslSetListenerVelocity(temp.x,temp.y,temp.z);

		Ogre::Vector3 temp2 = m_pListener->getOri() * Ogre::Vector3::NEGATIVE_UNIT_Z;
		temp = m_pListener->getOri() * Ogre::Vector3::UNIT_Y;
		fslSetListenerOrientation(temp2.x,temp2.y,temp2.z,
									temp.x,temp.y,temp.z);
	}

	fslUpdate();
}

SoundListener* SoundSystem::createSoundListener(const Ogre::Quaternion& ori,const Ogre::Vector3& position,const Ogre::Vector3& velocity)
{
	if(m_pListener)
		delete m_pListener;
	m_pListener = 0;
	m_pListener = new SoundListener(position,ori,velocity);
	return m_pListener;
}

void SoundSystem::updateSoundListener(const Ogre::Quaternion& ori,const Ogre::Vector3& position,const Ogre::Vector3& velocity)
{
	m_pListener->setPos(position);
	m_pListener->setVel(velocity);
	m_pListener->setOri(ori);
}

void SoundSystem::setVolume(const float& volume)
{
	fslSetVolume(volume);
}

void SoundSystem::togglePauseAll()
{
	if(m_bPauseAll)
	{
		fslSoundUnPauseAllSounds();
		SoundList::iterator iter;
		for(iter = m_Sounds.begin(); iter != m_Sounds.end(); iter++)
		{
			fslSoundPlay(*iter);
		}
	}
	else
	{
		fslSoundPauseAllSounds();
		SoundList::iterator iter;
		for(iter = m_Sounds.begin(); iter != m_Sounds.end(); iter++)
		{
			fslSoundPause(*iter);
		}
	}
	m_bPauseAll = !m_bPauseAll;
}